/*************************************************************************
	> File Name: myled.c
	> Author: life
	> Created Time: 2021年03月27日 星期六 19时02分23秒
 ************************************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>

#define BCM2711_GPIO_BASE  0xfe200000
#define DEVICE_NAME     "rpi-leds"  /* 加载模式后，执行”cat /proc/devices”命令看到的设备名称 */
static unsigned int * gpio_va;
static int major = 0; //主设备号

static struct class *leds_class;
static struct device *leds_class_devs[4];

#define LED1	3
#define LED2	4
#define LED3	17
//对 int 型指针操作 +1 等于地址 +4 所以要先 /4
#define GPFSEL0 (*(volatile unsigned int *)(gpio_va + (0x0/4)))
#define GPFSEL1 (*(volatile unsigned int *)(gpio_va + (0x4/4)))
#define GPFSEL2 (*(volatile unsigned int *)(gpio_va + (0x8/4)))
#define GPFSEL3 (*(volatile unsigned int *)(gpio_va + (0xc/4)))
#define GPFSEL4 (*(volatile unsigned int *)(gpio_va + (0x10/4)))
#define GPFSEL5 (*(volatile unsigned int *)(gpio_va + (0x14/4)))
#define GPSET0  (*(volatile unsigned int *)(gpio_va + (0x1c/4)))	//置位寄存器
#define GPSET1  (*(volatile unsigned int *)(gpio_va + (0x20/4)))
#define GPCLR0  (*(volatile unsigned int *)(gpio_va + (0x28/4)))	//清零寄存器
#define GPCLR1  (*(volatile unsigned int *)(gpio_va + (0x2c/4)))
#define GPLEV0	(*(volatile unsigned int *)(gpio_va + (0x34/4)))	//类似与STM32的IDR
#define GPLEV1	(*(volatile unsigned int *)(gpio_va + (0x38/4)))	//类似与STM32的IDR

static int rpi4_leds_open(struct inode *inode, struct file *file)
{
	//选择树莓派GPIO3 4 17来做实验
	int minor = MINOR(inode->i_rdev);
	switch(minor)
	{
		case 0: /* /dev/leds */
		{
			GPFSEL0 &= ~(7<<(3*LED1)); //清零 GPIO3   每3bit控制一个GPIO
			GPFSEL0 |= (1<<(3*LED1));  //置1 设置为输出模式 001 = GPIO Pin 3 is an output

			GPFSEL0 &= ~(7<<(3*LED2)); //清零 GPIO4   每3bit控制一个GPIO
			GPFSEL0 |= (1<<(3*LED2));  //置1 设置为输出模式 001 = GPIO Pin 3 is an

			GPFSEL1 &= ~(7<<(3*(LED3 - 10))); //清零 GPIO17   每3bit控制一个GPIO
			GPFSEL1 |= (1<<(3*(LED3 - 10)));  //置1 设置为输出模式 001 = GPIO Pin 3 is an
			break;
		}
		case 1: /* /dev/led1 */
		{
			
			GPFSEL0 &= ~(7<<(3*LED1)); //清零 GPIO3   每3bit控制一个GPIO
			GPFSEL0 |= (1<<(3*LED1));  //置1 设置为输出模式 001 = GPIO Pin 3 is an output
			break;
		}
		case 2: /* /dev/led2 */
		{
			
			GPFSEL0 &= ~(7<<(3*LED2)); //清零 GPIO4   每3bit控制一个GPIO
			GPFSEL0 |= (1<<(3*LED2));  //置1 设置为输出模式 001 = GPIO Pin 3 is an
			break;
		}
		case 3: /* /dev/led3 */
		{
			GPFSEL1 &= ~(7<<(3*(LED3 - 10))); //清零 GPIO17   每3bit控制一个GPIO
			GPFSEL1 |= (1<<(3*(LED3 - 10)));  //置1 设置为输出模式 001 = GPIO Pin 3 is an
			break;
		}
	}
	printk(KERN_INFO"respberry leds open\n");
    return 0;
}

static ssize_t rpi4_leds_read (struct file *file, char __user *buff, size_t count, loff_t *offp)
{
	int value = 0;
	int reg = GPLEV0;
	printk(KERN_INFO"rpi4_leds_read \n");
	
	if(reg & ( 1<<LED1 ))
		value |= 1 << 0;
	else
		value &= ~(1 << 0);
	if(reg & ( 1<<LED2 ))
		value |= 1 << 1;
	else
		value &= ~(1 << 1);
	if(reg & ( 1<<LED3 ))
		value |= 1 << 2;
	else
		value &= ~(1 << 2);

	if ( copy_to_user(buff, (const void *)&value, 1) !=0 )
	{
		printk(KERN_INFO"copy_to_user error \n");
		return -1;
	}
	return 0;
}

static ssize_t rpi4_leds_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
	int minor = MINOR(file->f_inode->i_rdev);
	char val;
	if( copy_from_user(&val, buf, 1) != 0)
	{
		printk(KERN_INFO"copy_from_user error \n");
		return -1;
	}
	switch (minor)
	{
		case 0: /* /dev/leds */
		{
			if(val)
			{
				GPSET0 |= 1<<LED1;
				GPSET0 |= 1<<LED2;
				GPSET0 |= 1<<LED3;
			}
			else
			{
				GPCLR0 |= 1<<LED1;
				GPCLR0 |= 1<<LED2;
				GPCLR0 |= 1<<LED3;
			}
			break;
		}
		case 1: /* /dev/led1 */
		{
			
			if(val)
			{
				GPSET0 |= (1<<LED1);
				printk(KERN_INFO"[%s][%d] GPSET0:%x \n",__func__,__LINE__,GPSET0);
			}
			else
			{
				GPCLR0 |= (1<<LED1);
				printk(KERN_INFO"[%s][%d] GPCLR0:%x \n",__func__,__LINE__,GPCLR0);
			}
			
			
			break;
		}
		case 2: /* /dev/led2 */
		{
			if(val)
			{
				GPSET0 |= (1<<LED2);
				printk(KERN_INFO"[%s][%d]GPSET0: %x \n",__func__,__LINE__,GPSET0);
			}
			else
			{
				GPCLR0 |= (1<<LED2);
				printk(KERN_INFO"[%s][%d]GPCLR0: %x \n",__func__,__LINE__,GPCLR0);
			}
			break;
		}
		case 3: /* /dev/led3 */
		{
			if(val)
			{
				GPSET0 |= (1<<LED3);
				printk(KERN_INFO"[%s][%d] :GPSET0:%x \n",__func__,__LINE__,GPSET0);
			}
			else
			{
				GPCLR0 |= (1<<LED3);
				printk(KERN_INFO"[%s][%d] GPCLR0:%x \n",__func__,__LINE__,GPCLR0);
			}
			break;
		}
	}
	return 1;
}

/* 这个结构是字符设备驱动程序的核心
 * 当应用程序操作设备文件时所调用的open、read、write等函数，
 * 最终会调用这个结构中指定的对应函数
 */
static struct file_operations rpi4_leds_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏，推向编译模块时自动创建的__this_module变量 */
    .open   =   rpi4_leds_open,     
	.read	=	rpi4_leds_read,	   
	.write	=	rpi4_leds_write,	   
};

static int __init leds_init(void)
{
	 /* 注册字符设备
     * 参数为主设备号、设备名字、file_operations结构；
     * 这样，主设备号就和具体的file_operations结构联系起来了，
     * 操作主设备为LED_MAJOR的设备文件时，就会调用s3c24xx_leds_fops中的相关成员函数
     * LED_MAJOR可以设为0，表示由内核自动分配主设备号
     */
	int minor;
	
    gpio_va = ioremap(BCM2711_GPIO_BASE, 0xF0);
	if (!gpio_va) {
		return -EIO;
	}
	printk (KERN_INFO"global_gpio = 0x%lx\n", (unsigned int)gpio_va);
	major = register_chrdev(major, DEVICE_NAME, &rpi4_leds_fops);
	printk(KERN_INFO "major ; %d\n",major);
	if (major < 0) {
		printk(DEVICE_NAME " can't register major number\n");
		return major;
    }
	
	leds_class = class_create(THIS_MODULE, "myleds");//注册class 让代码在dev自动生成设备
	if (IS_ERR(leds_class))
		return PTR_ERR(leds_class);

	leds_class_devs[0] = device_create(leds_class, NULL, MKDEV(major, 0), NULL, "leds");

	for (minor = 1; minor < 4; minor++)
	{
		leds_class_devs[minor] = device_create(leds_class, NULL, MKDEV(major, minor), NULL, "led%d", minor);
		if (unlikely(IS_ERR(leds_class_devs[minor])))
			return PTR_ERR(leds_class_devs[minor]);
	}
	printk(KERN_INFO"raspberry leds initialized\n");
	return 0;
}

static void __exit leds_exit(void)
{
	int minor;
	for(minor = 0; minor < 4; minor++ )
		device_unregister(leds_class_devs[minor]);
	class_destroy(leds_class);
	unregister_chrdev(major, DEVICE_NAME);
	iounmap(gpio_va);
	printk(KERN_INFO"leds_exit exit\n");
}
module_init(leds_init);
module_exit(leds_exit);

/* 描述驱动程序的一些信息，不是必须的 */
MODULE_AUTHOR("ChenBlue");
MODULE_VERSION("0.1.0");
MODULE_DESCRIPTION("raspberry LED Driver");
MODULE_LICENSE("GPL");


